// Handle an initial down. 注释1 if (actionMasked == MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. cancelAndClearTouchTargets(ev); resetTouchState(); }
// Check for interception.注释2 finalboolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { finalboolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches.注释3 intercepted = true; }
/** * Resets all touch state in preparation for a new cycle. * 重置所有状态以进入新周期 */ privatevoidresetTouchState(){ clearTouchTargets(); resetCancelNextUpFlag(this); mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; mNestedScrollAxes = SCROLL_AXIS_NONE; }
//获得所有子view的数量 finalint childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { finalfloat x = ev.getX(actionIndex); finalfloat y = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. //创建了一个用于向子view分发touch事件的list final ArrayList<View> preorderedList = buildTouchDispatchChildList(); finalboolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; //遍历子view for (int i = childrenCount - 1; i >= 0; i--) { //获取子view的脚标 finalint childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); //通过脚标拿到子view对象 final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex);
// If there is a view that has accessibility focus we want it // to get the event first and if not handled we will perform a // normal dispatch. We may do a double iteration but this is // safer given the timeframe. if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } //如果view在播放动画或者点击事件的坐标不在该view区域内 则遍历下一个 //否则交由它处理 if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } //从单链表中拿到这个值为child的TouchTarget对象 newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; }
resetCancelNextUpFlag(child); //调用dispatchTransformedTouchEvent方法,用于判断子view是否拦截事件 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. //记录时间 mLastTouchDownTime = ev.getDownTime(); //记录子view的脚标 if (preorderedList != null) { // childIndex points into presorted list, find original index for (int j = 0; j < childrenCount; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } //记录事件的坐标 mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); //为新触摸目标赋值 将child添加进链表并返回值 newTouchTarget = addTouchTarget(child, idBitsToAssign); //已经分发给新的触摸目标设置为true alreadyDispatchedToNewTouchTarget = true; //跳出循环,因为处理事件的view已经有了 break; }
// The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } //清空存放view的list if (preorderedList != null) preorderedList.clear(); }
privatebooleandispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits){ finalboolean handled;
// Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. finalint oldAction = event.getAction(); if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } ··· }
// Dispatch to touch targets. if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); }
if ((viewFlags & ENABLED_MASK) == DISABLED) {//注释1 if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return clickable; } //注释2 if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { returntrue; } } }
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { // This is a tap, so remove the longpress check removeLongPressCallback();
// Only perform take click actions if we were in the pressed state if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClickInternal(); } } } ... } ... break; } ... returntrue; }
privatebooleanperformClickInternal(){ // Must notify autofill manager before performing the click actions to avoid scenarios where // the app has a click listener that changes the state of views the autofill service might // be interested on. notifyAutofillManagerOnClick();
return performClick(); }
publicbooleanperformClick(){ // We still need to call this method to handle the cases where performClick() was called // externally, instead of through performClickInternal() notifyAutofillManagerOnClick();
finalboolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; }